package org.obsidianrad.utils.extensions;


import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Properties;

import org.obsidianrad.utils.FileUtils;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
import org.springframework.util.ObjectUtils;

import com.ibatis.common.xml.NodeletException;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.builder.xml.SqlMapConfigParser;
import com.ibatis.sqlmap.engine.builder.xml.SqlMapParser;
import com.ibatis.sqlmap.engine.builder.xml.XmlParserState;

public class ObsidianSqlMapClientFactoryBean extends SqlMapClientFactoryBean {

	/**
	 * Effettuiamo l'override di questo metodo per poter iniettare le mapping locations dentro la config location, 
	 * in modo da poter far scattare il meccanismo del Discriminator
	 */
	@Override
	protected SqlMapClient buildSqlMapClient(Resource[] configLocations, Resource[] mappingLocations, Properties properties) throws IOException {

		if (ObjectUtils.isEmpty(configLocations)) {
			throw new IllegalArgumentException("At least 1 'configLocation' entry is required");
		}

		Arrays.sort(mappingLocations, new DirAlphaComparator());
		
		SqlMapClient client = null;
		SqlMapConfigParser configParser = new SqlMapConfigParser();
		for (int i = 0; i < configLocations.length; i++) {
			InputStream is = configLocations[i].getInputStream();
		
			String sqlConfig = FileUtils.getStringFromIS(is);
			String sqlConfigOpen = sqlConfig.replace("</sqlMapConfig>", "");
			for(int k=0; k<mappingLocations.length; k++){
				String url= mappingLocations[k].getURL().getFile();
				int indexOf = url.indexOf("it/pulsetech");
				String bUrl = url.substring(indexOf);
				sqlConfigOpen += "<sqlMap resource=\""+ bUrl +"\" />";
			}
			sqlConfigOpen += "</sqlMapConfig>";
			
			InputStream is2 = FileUtils.getISFromString(sqlConfigOpen);
			mappingLocations = null;
			
			try {
				client = configParser.parse(is2, properties);
			}
			catch (RuntimeException ex) {
				throw new NestedIOException("Failed to parse config resource: " + configLocations[i], ex.getCause());
			}
		}

		if (mappingLocations != null) {
			SqlMapParser mapParser = SqlMapParserFactory.createSqlMapParser(configParser);
			for (int i = 0; i < mappingLocations.length; i++) {
				try {
					mapParser.parse(mappingLocations[i].getInputStream());
				}
				catch (NodeletException ex) {
					throw new NestedIOException("Failed to parse mapping resource: " + mappingLocations[i], ex);
				}
			}
		}

		return client;
	}

	/**
	 * Inner class to avoid hard-coded iBATIS 2.3.2 dependency (XmlParserState class).
	 */
	private static class SqlMapParserFactory {

		public static SqlMapParser createSqlMapParser(SqlMapConfigParser configParser) {
			// Ideally: XmlParserState state = configParser.getState();
			// Should raise an enhancement request with iBATIS...
			XmlParserState state = null;
			try {
				Field stateField = SqlMapConfigParser.class.getDeclaredField("state");
				stateField.setAccessible(true);
				state = (XmlParserState) stateField.get(configParser);
			}
			catch (Exception ex) {
				throw new IllegalStateException("iBATIS 2.3.2 'state' field not found in SqlMapConfigParser class - " +
						"please upgrade to IBATIS 2.3.2 or higher in order to use the new 'mappingLocations' feature. " + ex);
			}
			return new SqlMapParser(state);
		}
	}

	//////////////////////////////////////////////////DirAlphaComparator
	// To sort directories before files, then alphabetically.
	private class DirAlphaComparator implements Comparator<Resource> {

	    // Comparator interface requires defining compare method.
	    public int compare(Resource filea, Resource fileb) {
	        //... Sort directories before files,
	        //    otherwise alphabetical ignoring case.
	    	try {
				String urlA =  filea.getURL().getFile();
				String urlB =  fileb.getURL().getFile();
				return urlA.compareToIgnoreCase(urlB);
			} catch (IOException e) {
				e.printStackTrace();
			}
			return 0;
		}
	}

}
